// 配置模块：负责读取服务器端的配置文件，设置默认值
// read_server_config：读取服务器配置文件 server.conf，设置服务器端口、日志目录和日志文件名。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "utils.h"
/**
 *  读取服务器配置文件，若文件不存在则使用默认配置。
 * 
 * 该函数会尝试打开服务器配置文件，读取其中的配置项。若配置文件不存在，
 * 则使用预设的默认配置。支持读取的配置项包括服务器端口、日志目录和日志文件名。
 * 
 *  config：指向 ServerConfig 结构体的指针，用于存储读取到的配置信息。
 */
int read_server_config(ServerConfig *config) { 
    // 设置默认值
    config->port = 9999;
    strcpy(config->log_dir, "logs");
    strcpy(config->log_file, "chat_server");
    config->max_clients = 6;

    
    // 尝试以只读模式打开配置文件
    FILE *file = fopen(CONFIG_FILE, "r");
    // 检查文件是否成功打开
    if (!file) {
        // 若文件未找到，打印提示信息并使用默认设置
        printf("未找到配置文件 %s，使用默认设置\n", CONFIG_FILE);
        return 1; // 返回非零值表示错误
    }
    // 若文件打开成功，开始读取配置项


    // 定义缓冲区，用于存储从文件中读取的每一行内容
    //定义 line 缓冲区，使用 fgets 函数逐行读取配置文件内容。
    // 调用 trim_string 函数去除行首和行尾的空白符。
    //若行为空行或注释行（以 # 开头），则跳过当前行。
    //使用 strchr 函数查找行内注释 #，
    //若找到则将其替换为字符串结束符 '\0'，截断注释部分。
    char line[256];
    // 逐行读取配置文件，直到文件结束
    while (fgets(line, sizeof(line), file)) {
        trim_string(line); // 去除行首和行尾的空白符
         // 跳过空行
        if (strlen(line)== 0) {   
            continue;
        }
        // 跳过注释行和空行
        if(line[0] == '#' || line[0] == '\0') { 
            continue;
        }
        // 移除行内注释，防止出现解析后为9999 #服务端口的情况
        //shtrchr查找#字符
        char *comment = strchr(line, '#');
        if (comment) {
            *comment = '\0';
        }


        //解析键值对
        //从配置文件读取的单行内容里分割出键值对
        // 使用strchr在line里查找 = 首次出现的位置
        //sep：指向 line 字符串中首次出现 = 字符位置的指针。
        //若 line 中不存在 = 字符，sep 为 NULL。
        //if判断sep 是否为 NULL。
        //若不为 NULL，意味着 line 中存在 = 字符，可进行键值对分割操作。
        //若为NULL，则打印提示信息并跳过当前行。
        char *sep = strchr(line, '=');
        if (sep) {
            // 分割键值对
            //将 sep 指针指向的字符（即 =）替换成字符串结束符 '\0'。
            //这样，line 字符串就被截断为 = 之前的部分，作为键。
            //line 字符串中 = 字符之后的部分（即 sep + 1），则作为值。
            //调用 trim_string 函数去除键和值两端的空白符。
            *sep = '\0'; 
            char *key = line;
            char *value = sep + 1;
            // 检查值是否为空
            if (*value == '\0') {
                printf("配置项值为空: %s\n", key);
                continue;
            }
            // 修剪键和值两端的空格
            trim_string(key);
            trim_string(value);
            

            // 处理配置项
            // 从当前行中解析出键和值，格式为 "键 = 值"
            //使用 sscanf 函数从 line 中解析键和值，若成功解析出两个参数则继续处理。
            //根据键的不同，将对应的值赋给 config 结构体的相应成员。
            // 若键未被识别，打印未识别配置项的提示信息。
            // /若 line 中不存在 = 字符，打印配置文件格式错误的提示信息。
            //%99[^\n] 匹配最多99个非换行符字符，直到遇到换行符
            if (sscanf(line, "%49s = %99[^\n]", key, value) == 2) {
                // 处理配置项
                if (strcmp(key, "Port") == 0) {
                    config->port = atoi(value); // 若键为 "Port"，将值转换为整数并赋值给服务器端口
                } 
                else if (strcmp(key, "LogDir") == 0) {  // 若键为 "LogDir"，将值复制到日志目录字段
                    strcpy(config->log_dir, value);
                } 
                else if (strcmp(key, "LogFile") == 0) { // 若键为 "LogFile"，将值复制到日志文件名字段
                    strcpy(config->log_file, value);
                } 
                else if (strcmp(key, "MaxClients") == 0) {
                    config->max_clients = atoi(value); // 若键为 "MaxClients"，将值转换为整数并赋值给最大客户端数
                } 
                else {
                    // 对于未识别的配置项，给出提示信息
                    printf("未识别的配置项: %s = %s\n", key, value);
                }
            }
        }
        else {
            printf("配置文件格式错误: %s\n", line);
        }
    }


    // 关闭已打开的配置文件
    //返回0表示配置文件读取成功
    fclose(file);
    return 0;
}

